#include "cLogger.h"

// function for using in thread
DWORD WINAPI outerDumpFunc(LPVOID lpParam);

cLogger::cLogger() {
	ring_buffer = new char[LOG_RINGBUFFERSIZE];
	rb_dumped_pos = rb_cur_position = 0;
	waitForDump = true;
	rb_occupied_space = 0;
	
	semaphore = 0;
	DumpHalf = 0;
	empty = 0;

	// thread
	hDumpThread = CreateThread( 
            NULL,                   // default security attributes
            0,                      // use default stack size  
			outerDumpFunc,			// thread function name
            this,					// argument to thread function 
            0,                      // use default creation flags 
            NULL);
	if(hDumpThread == NULL) {
		// ERROR
	}

	// synhro
	hMutex = CreateMutex( 
        NULL,              // default security attributes
        FALSE,             // initially not owned
        NULL);             // unnamed mutex
	if(hMutex == NULL) {
		// ERROR
	}

	hDumpEvent = CreateEvent( 
        NULL,               // default security attributes
        TRUE,               // manual-reset event
        FALSE,              // initial state is nonsignaled
        NULL				// object name(unnamed)
        ); 
	if(hDumpEvent == NULL) {
		// ERROR
	}
	hDumpedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
	if(hDumpedEvent == NULL) {
		// ERROR
	}
	hSemaphoreEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
	if(hDumpedEvent == NULL) {
		// ERROR
	}

	// file
	f = fopen("1.txt", "w");

	counter = 0;
	counter2 = 0;
}

cLogger::~cLogger() {
	// send message to hDumpThread to exit
	if(WaitForSingleObject(hSemaphoreEvent, INFINITE) != WAIT_OBJECT_0) {
		// ERROR
	}

	if(WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
		_message = -1;
		//_rb_cur_position = rb_cur_position;
		SetEvent(hDumpEvent);
		ReleaseMutex(hMutex);
	} else {
		// ERROR
	}
	
	WaitForSingleObject(hDumpThread, INFINITE);
	
	fclose(f);

	if(counter2*9 != counter)
		int y = 0;

	delete [] ring_buffer;

	// CLOSE ALL HEANDLES
}

void cLogger::SetWaitingOption(bool w) {
	waitForDump = w;
}

long cLogger::diff(long a) {
	long a1 = a - rb_dumped_pos;
	if(a1 < 0)
		return LOG_RINGBUFFERSIZE + a1;
	else
		return a1;
	
}

void cLogger::PutDebug(char *message) {
	while(1) {
		if(diff(rb_cur_position) > LOG_RINGBUFFERSIZE/2) {
			// FLUSHER ON
			if(!DumpHalf) {
				_InterlockedExchange(&DumpHalf, 1);
				if(WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
					if(!empty) {
						_message = 2;
						_rb_cur_position = rb_cur_position;
						
						SetEvent(hDumpEvent);
					}
					ReleaseMutex(hMutex);
				} else {
					// ERROR
				}
			}
		}
		
		size_t size = strlen(message);
		
		if(diff(rb_cur_position+size) > (3*LOG_RINGBUFFERSIZE)/4) {
			// STOP
			// FLUSHER ON
			// send message to FLUSHER with parameter (rb_cur_position)
			if(WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
				_message = 1;
				//_rb_cur_position = rb_cur_position;
				
				SetEvent(hDumpEvent);
				ResetEvent(hDumpedEvent);
				ReleaseMutex(hMutex);
			} else {
				// ERROR
			}
			if(waitForDump) {
				// WAITING
				if(WaitForSingleObject(hDumpedEvent, INFINITE) != WAIT_OBJECT_0) {
					// ERROR
				}
			} else {
				return;
			}
		}
		
		// SEMAPHORE++
		_InterlockedIncrement(&semaphore);
		ResetEvent(hSemaphoreEvent);

		unsigned int destination = _InterlockedExchangeAdd(&rb_cur_position, size);
		_InterlockedExchange(&rb_cur_position, rb_cur_position & LOG_RING);
		destination &= LOG_RING;

		if(diff(destination + size) > (3*LOG_RINGBUFFERSIZE)/4) {
			// STOP
			_InterlockedExchangeAdd(&rb_cur_position, -((long)size));
			// SEMAPHORE--
			_InterlockedDecrement(&semaphore);
			if(!semaphore)
				SetEvent(hSemaphoreEvent);

			// send message to FLUSHER with parameter (rb_cur_position)
			if(WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
				_message = 3;
				//_rb_cur_position = rb_cur_position;

				SetEvent(hDumpEvent);
				ResetEvent(hDumpedEvent);
				ReleaseMutex(hMutex);
			} else {
				// ERROR
			}
			if(waitForDump) {
				// WAITING
				if(WaitForSingleObject(hDumpedEvent, INFINITE) != WAIT_OBJECT_0) {
					// ERROR
				}
			} else {
				return;
			}
		} else {
			// WRITE & EXIT
			_InterlockedIncrement(&counter2);
			if(destination + size > LOG_RINGBUFFERMAXINDEX) {
				memcpy(&ring_buffer[destination], message, (LOG_RINGBUFFERSIZE - destination)*sizeof(char));
				memcpy(&ring_buffer[0], &message[LOG_RINGBUFFERSIZE - destination], (size - (LOG_RINGBUFFERSIZE - destination))*sizeof(char));
			} else {
				memcpy(&ring_buffer[destination], message, size*sizeof(char));
			}
			_InterlockedExchange(&empty, 0);
			// SEMAPHORE--
			_InterlockedDecrement(&semaphore);
			if(!semaphore)
				SetEvent(hSemaphoreEvent);

			return;
		}
	} // while(1)
}

bool cLogger::Dump(long in) {
	if(empty) {
		if(_message == -1) {
			if(rb_cur_position == rb_dumped_pos)
				return true;
		} else {
			return true;
		}
	}

	long rb_cur = in & LOG_RING;

	_InterlockedExchange(&rb_dumped_pos, rb_dumped_pos & LOG_RING);

	if(rb_cur > rb_dumped_pos) {
		// write from rb_dumped_pos to rb_cur
		fwrite(&ring_buffer[rb_dumped_pos], rb_cur - rb_dumped_pos, 1, f);
		counter += rb_cur - rb_dumped_pos;
	} else {
		// write from rb_dumped_pos to LOG_RINGBUFFERSIZE
		// write from 0 to rb_cur
		fwrite(&ring_buffer[rb_dumped_pos], LOG_RINGBUFFERSIZE - rb_dumped_pos, 1, f);
		fwrite(&ring_buffer[0], rb_cur, 1, f);
		counter += LOG_RINGBUFFERSIZE - rb_dumped_pos;
		counter += rb_cur;
	}

	_InterlockedExchange(&rb_dumped_pos, rb_cur);
	_InterlockedExchange(&empty, 1);

	return true;
}

DWORD WINAPI outerDumpFunc(LPVOID lpParam) {
	cLogger* p = (class cLogger*) lpParam;
	
	DWORD dwWaitResult;
	
	while(1) {
		dwWaitResult = WaitForSingleObject(p->hDumpEvent, INFINITE);
		if(dwWaitResult == WAIT_OBJECT_0) {

			dwWaitResult = WaitForSingleObject(p->hMutex, INFINITE);
			if(dwWaitResult == WAIT_OBJECT_0) {
				if(WaitForSingleObject(p->hSemaphoreEvent, INFINITE) != WAIT_OBJECT_0) {
					// ERROR
				}
				long a = p->rb_cur_position;
				if(p->_message == 2)	a = p->_rb_cur_position;

				if(!p->Dump(a)) {
					// ERROR
				}
				if(p->DumpHalf)
					_InterlockedExchange(&(p->DumpHalf), 0);

				if(p->_message == -1)	return 0;

				SetEvent(p->hDumpedEvent);
				ResetEvent(p->hDumpEvent);
				ReleaseMutex(p->hMutex);
			} else {
				// ERROR
			}
		} else {
			// ERROR
		}
	}

	return 0;
}